#include <drivers.h>
#include <drivers/pin.h>

#include <units/work_queue.h>
#include <clock.h>

struct led_dev {
	struct device *pin;
	base_t pin_num;
	struct device *led_pin;
	base_t led_pin_num;
	struct device *led;
	struct work_queue_item item;
};

static struct led_dev led_dev;

static void led_on_off_entry(void *args)
{
	struct led_dev *led_dev = (struct led_dev *)args;
	mdelay(10);
	if (hw_pin_read(led_dev->pin_num) == 1)
		device_control(led_dev->led, LED_CMD_TOOGLE, NULL);
}

static void led_on_off(void *args)
{
	struct led_dev *led_dev = (struct led_dev *)args;
	work_queue_attach_item(&led_dev->item);
}

static err_t led_init(struct device *dev)
{
	led_dev.led_pin = device_find(PIN_DEVICE_NAME);
	led_dev.led_pin_num = hw_pin_get("PB5");
	hw_pin_mode(led_dev.led_pin_num, PIN_MODE_OUTPUT);
	hw_pin_write(led_dev.led_pin_num, 1);

	led_dev.pin = device_find(PIN_DEVICE_NAME);
	led_dev.pin_num = hw_pin_get("PA0");
	hw_pin_mode(led_dev.pin_num, PIN_MODE_INPUT_PULL_DOWN);
	hw_pin_attach_irq(led_dev.pin_num, PIN_IRQ_MODE_RISING, led_on_off, &led_dev, 0x05);
	hw_pin_irq_enable(led_dev.pin_num, true);

	led_dev.item.call = led_on_off_entry;
	led_dev.item.args = &led_dev;

	return 0;
}

static err_t led_open(struct device *dev, uint16_t oflag)
{
	return 0;
}

static err_t led_control(struct device *dev, int cmd, void *args)
{
	static uint8_t led_status = 0;

	switch (cmd) {
	case LED_CMD_OFF:
		hw_pin_write(led_dev.led_pin_num, 1);
		break;
	case LED_CMD_ON:
		hw_pin_write(led_dev.led_pin_num, 0);
		break;
	case LED_CMD_TOOGLE:
		if (led_status)
			hw_pin_write(led_dev.led_pin_num, 1);
		else
			hw_pin_write(led_dev.led_pin_num, 0);

		/* 每次都将状态翻转 */
		led_status ^= 1;
		break;
	}

	return 0;
}

static struct device_ops led_ops = {
	.init = led_init,
	.open = led_open,
	.control = led_control
};

static struct device led = {
	.ops = &led_ops,
	.type = DEVICE_TYPE_CHAR
};

void led_device_init(void)
{
	led_dev.led = &led;
	device_register(&led, LED_NAME, DEVICE_FLAG_RW);
}
INIT_DEVICE_EXPORT(led_device_init);
